﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;


namespace ParticleTracker
{
    public enum SubDivisionStyle { OVERLAP, NONOVERLAP };
    public enum EdgeHandling { OVERLAP, MINIATURE };
    public class ParticleImageVelocimetry : ParticleTrackingMethod
    {
        Size win;
        int maxVel = 5;
        public SubDivisionStyle divStyle { set; get; }
        public EdgeHandling edgeStyle { set; get; }
        public double Overlap { set; get; }
        public ParticleImageVelocimetry(ParticleReel reel)
            : base(reel)
        {
            this.win = new Size(10,10);
            this.divStyle = SubDivisionStyle.OVERLAP;
            this.edgeStyle = EdgeHandling.OVERLAP;
            this.Overlap = 0.5;
        }
        public ParticleImageVelocimetry(ParticleReel reel, int maxVelocity, Size win)
            : base(reel)
        {
            this.maxVel = maxVelocity;
            this.win = win;
            this.divStyle = SubDivisionStyle.OVERLAP;
            this.edgeStyle = EdgeHandling.OVERLAP;
            this.Overlap = 0.5;
        }
        public List<Rectangle> subdivideFrame1(Rectangle frame)
        {
            List<Rectangle> subFrames1 = new List<Rectangle>();
            int Dx = 1;
            int Dy = 1;
            switch (divStyle)
            {
                case SubDivisionStyle.NONOVERLAP:
                    Dx = win.Width;
                    Dy = win.Height;
                    break;
                case SubDivisionStyle.OVERLAP:
                    Dx = (int)(Overlap * win.Width);
                    Dy = (int)(Overlap * win.Height);
                    break;
            }
            for (int i = 0; i < frame.Width; i = i + Dx)
            {
                for (int j = 0; j < frame.Height; j = j + Dy)
                {
                    Rectangle sub = new Rectangle(new Point(i, j), win);
                    if (frame.Contains(sub))
                    {
                        subFrames1.Add(sub);
                    }
                    else
                    {
                        switch (edgeStyle)
                        {
                            case EdgeHandling.MINIATURE:
                                goto EdgeMINIATURE;
                            case EdgeHandling.OVERLAP:
                                goto EdgeOVERLAP;
                        }
                    EdgeMINIATURE:
                        sub.Intersect(frame);
                        subFrames1.Add(sub);
                        continue;
                    EdgeOVERLAP:
                        if (sub.Right > frame.Right)
                        {
                            sub.X = frame.Right - sub.Width;
                        }
                        if (sub.Bottom > frame.Bottom)
                        {
                            sub.Y = frame.Bottom - sub.Height;
                        }
                        subFrames1.Add(sub);
                        continue;
                    }
                }
            }
            return subFrames1;
        }
        public List<List<Rectangle>> subdivideFrame2(Rectangle frame, List<Rectangle> subFrames1)
        {
            List<List<Rectangle>> subFrames2 = new List<List<Rectangle>>();
            for (int i = 0; i < subFrames1.Count; i++)
            {
                List<Rectangle> searchFrames = new List<Rectangle>();
                Rectangle sub1 = subFrames1[i];
                int j = 0;
                double th = 0;
                while (th < 2 * Math.PI)
                {
                    //THIS IS ARBITRARY. FIX IT!!!
                    th += Math.PI * 0.2;
                    Point p = sub1.Location;
                    // want half the window dimension on each side of the
                    // expected position based on the velocity to act as
                    // a cushion. Causes problems. test. MAYBE FIXED.
                    p.X += (int)(maxVel * Math.Cos(th));// - sub1.Width * 0.5);
                    p.Y += (int)(maxVel * Math.Sin(th));// - sub1.Height * 0.5);
                    //Console.WriteLine("p = " + p + "  th = " + th);
                    Rectangle r = new Rectangle(p, win);
                    //test to see if we've created a rectangle
                    //that is in the frame. A rectangle outside
                    //the frame would be useless for getting a
                    //region of the frame.
                    if (r.IntersectsWith(frame))
                    {
                        searchFrames.Add(r);
                        j = j + 1;
                    }
                }
                // If the rectangle is in the frame, 
                // even partially, we'll keep it and
                // chop off what is hanging outside
                // the frame.
                for (int k = 0; k < searchFrames.Count; k++)
                {
                    Rectangle r = searchFrames[k];
                    r.Intersect(frame);
                    searchFrames[k] = r;
                }
                // Some rectangles may have been chopped off
                // so that they occupy the same area, as in 
                // one rectangle fully engulfs another. It
                // isn't helpful to grab the smaller, engulfed
                // area twice, so we only want to keep the 
                // biggest rectangle that contains any smaller
                // ones.
                int m = 0;
                while (m < searchFrames.Count)
                {
                    bool remove = false;
                    Rectangle r1 = searchFrames[m];
                    for (int n = 0; n < searchFrames.Count; n++)
                    {
                        if (m != n)
                        {
                            Rectangle r2 = searchFrames[n];
                            if (r2.Contains(r1))
                            {
                                remove = true;
                                break;
                            }
                        }
                    }
                    // if we remove the rectangle, the higher 
                    // elements in the List<> will shift down,
                    // so if we increment m after a removal
                    // we will skip an element.
                    if (remove)
                    {
                        searchFrames.RemoveAt(m);
                        continue;
                    }
                    m = m + 1;
                }
                subFrames2.Add(searchFrames);
            }
            return subFrames2;
        }
        public override void Run()
        {
            Rectangle frame = reel.Frames[0].RectangleFull;
            List<Rectangle> subFrames1 = subdivideFrame1(frame);
            List<List<Rectangle>> subFrames2 = subdivideFrame2(frame, subFrames1);
            FrameCouple fc = new FrameCouple(subFrames1, subFrames2);
            for (int i = 0; i < reel.Frames.Count - 1; i++)
            {
                fc.frame1 = reel.Frames[i];
                fc.frame2 = reel.Frames[i + 1];
                fc.findVelocities();
                for(int j = 0; j<subFrames1.Count; j++)
                {
                    MovingRectangle mr = new MovingRectangle(subFrames1[j]);
                    mr.velocity = fc.velocities[j];
                    reel.Frames[i].Pieces.Add(mr);
                }
                Console.WriteLine("oh billy. Why won't you work? " + i);
            }
            
        }
        public Point getCenroid(Rectangle r)
        {
            Point p = r.Location;
            p.X += (int)(r.Width * 0.5);
            p.Y += (int)(r.Height * 0.5);
            return p;
        }
        public class MovingRectangle : Piece
        {
            Rectangle rect;
            public MovingRectangle(Rectangle r)
            {
                this.rect = r;
                this.Center = new Point(r.X + r.Width / 2, r.Y + r.Height / 2);
            }
            public void SetCenter()
            {
                this.Center = new Point();
            }
        }
    }
}